// Paketinė OS, II variantas
// Edmundas Drūteika
// Vytautas Bačiulis

import java.util.Scanner;
import java.io.File;

class Main {
    public static void main(String args[]) {
        System.out.println("Kuriama reali mašina...");
        Machine machine = new Machine();
        System.out.println("Reali mašina sukurta sėkmingai!\n");

        Scanner scan = new Scanner(System.in);
        String command;

        while (true) {
            System.out.println("Komandos:");
            System.out.println("* CREATEVM - sukuria virtualią mašiną.");
            System.out.println("* QUIT - nutraukia programos darbą.");

            System.out.println("\nĮveskite komandą:");
            command = scan.nextLine();
            if (command.toLowerCase().equals("createvm")) {
                System.out.println("\nKuriama virtuali mašina...");
                VirtualMachine VMachine = new VirtualMachine(machine.getVMMemory());
                System.out.println("Virtuali mašina sukurta sėkmingai!");

                try {
                    File resultFile = new File("OUT.txt");
                    resultFile.delete();
                }
                catch (SecurityException e) {
                }

                VMMenu(machine, VMachine);
            }
            else if (command.toLowerCase().equals("quit")) {
                break;
            }
            else {
                System.out.println("Tokia komanda neegzistuoja!");
            }
        }

    }

    static void VMMenu(Machine machine, VirtualMachine VMachine) {
        Scanner scan = new Scanner(System.in);
        String command;
        boolean compilationSuccessful = false;

        printHelpMenu();
        while (true) {
            System.out.println("\nĮveskite komandą:");
            command = scan.nextLine();
            if (command.toLowerCase().equals("compile")) {
                Compiler.removeValues(VMachine.getMemoryVM());
                machine.changePI(0L);
                machine.changeIOI(0L);
                machine.changeCC(0L);
                VMachine.changeCC(0L);
                System.out.println("\nĮveskite programos failo pavadinimą:");
                command = scan.nextLine();

                try {
                    File applicationFile = new File(command);
                    if (!applicationFile.canRead()) {
                        System.out.println("\nKlaida! Nepavyksta nuskaityti failo!");
                        continue;
                    }
                }
                catch (SecurityException e) {
                    System.out.println("Error: " + e.getMessage());
                    continue;
                }

                if (Compiler.compile(command, VMachine.getMemoryVM())) {
                    System.out.println("Failas sukompiliuotas sėkmingai!");
                    compilationSuccessful = true;
                } else {
                    System.out.println("Kompiliuojant failą įvyko klaida!");
                    compilationSuccessful = false;
                }
            }
            else if (command.toLowerCase().equals("help")) {
                printHelpMenu();
            }
            else if (command.toLowerCase().equals("destroy")) {
                Compiler.removeValues(VMachine.getMemoryVM());
                break;
            }
            else if (command.toLowerCase().equals("machinememory")) {
                printRealMachineMemory(machine);
            }
            else if (command.toLowerCase().equals("vmmemory")) {
                printVirtualMachineMemory(VMachine);
            }
            else if (command.toLowerCase().equals("machineregisters")) {
                printRealMachineRegisters(machine);
            }
            else if (command.toLowerCase().equals("vmregisters")) {
                printVirtualMachineRegisters(VMachine);
            }
            else if (command.toLowerCase().equals("step")) {
                if (machine.getPI() == 1){
                    System.out.println("\nĮvyko programinio kodo klaida! Programa negali būti toliau vykdoma!\n");
                    continue;
                }
                if (machine.getIOI() == 1){
                    System.out.println("\nĮvyko įvedimo/išvedimo kodo klaida! Programa negali būti toliau vykdoma!\n");
                    continue;
                }
                if (!Interpretator.run(machine, VMachine)) {
                    System.out.println("\nPrograma užbaigta sėkmingai!\n");
                    continue;
                }
                if (machine.getPI() == 1){
                    System.out.println("\nĮvyko programinio kodo klaida! Programa negali būti toliau vykdoma!\n");
                    continue;
                }
                if (machine.getIOI() == 1){
                    System.out.println("\nĮvyko įvedimo/išvedimo kodo klaida! Programa negali būti toliau vykdoma!\n");
                    continue;
                }
            }
            else if (command.toLowerCase().equals("run")) {
                while (Interpretator.run(machine, VMachine)){
                    if (machine.getPI() == 1){
                        System.out.println("\nĮvyko programinio kodo klaida! Programa negali būti toliau vykdoma!\n");
                        break;
                    }
                    if (machine.getIOI() == 1){
                        System.out.println("\nĮvyko įvedimo/išvedimo kodo klaida! Programa negali būti toliau vykdoma!\n");
                        break;
                    }
                    if (!Interpretator.run(machine, VMachine)) {
                        System.out.println("\nPrograma užbaigta sėkmingai!\n");
                    }
                    if (machine.getPI() == 1){
                        System.out.println("\nĮvyko programinio kodo klaida! Programa negali būti toliau vykdoma!\n");
                        break;
                    }
                    if (machine.getIOI() == 1){
                        System.out.println("\nĮvyko įvedimo/išvedimo kodo klaida! Programa negali būti toliau vykdoma!\n");
                        break;
                    }

                }
            }
            else {
                System.out.println("Tokia komanda neegzistuoja!");
            }
        }
    }

    static void printHelpMenu() {
        System.out.println("\nKomandos:");
        System.out.println("* COMPILE - iškviečia kompiliatoriaus meniu.");
        System.out.println("* HELP - atspausdina pagalbos meniu.");
        System.out.println("* DESTROY - sunaikina virtualią mašiną.");
        System.out.println("* MACHINEMEMORY - atspausdina realios mašinos atmintį.");
        System.out.println("* VMMEMORY - atspausdina virtualios mašinos atmintį.");
        System.out.println("* MACHINEREGISTERS - atspausdina realios mašinos registrus.");
        System.out.println("* VMREGISTERS - atspausdina virtualios mašinos registrus.");
        System.out.println("* STEP - įvykdo einamąją kodo komandą.");
        System.out.println("* RUN - įvykdo visą programą.");
    }

    static void printRealMachineMemory(Machine machine) {
        String CSAddress, DSAddress;
        String CSValue, DSValue;

        int CS = (int) machine.getCS();
        int DS = (int) machine.getDS();

        LongObject[] memory = machine.getMemory();

        for (int i = 0; i < 128; i++) {
            CSValue = Long.toHexString(memory[i+CS].getValue());
            DSValue = Long.toHexString(memory[i+DS].getValue());
            CSAddress = Integer.toHexString(i + CS);
            DSAddress = Integer.toHexString(i + DS);

            if (CSAddress.length() == 1) {
                CSAddress = "00"+CSAddress;
            }
            else if (CSAddress.length() == 2) {
                CSAddress = "0"+CSAddress;
            }

            if (DSAddress.length() == 1) {
                DSAddress = "00"+DSAddress;
            }
            else if (DSAddress.length() == 2) {
                DSAddress = "0"+DSAddress;
            }


            while (true) {
                if (CSValue.length() == 8) break;
                else CSValue = "0"+CSValue;
            }

            while (true) {
                if (DSValue.length() == 8) break;
                else DSValue = "0"+DSValue;
            }
            if ((machine.getCC()-machine.getCS()) == i)
                System.out.println(CSAddress + ": \033[31m" + CSValue + "\033[0m        " + DSAddress + ": " + DSValue);
            else
                System.out.println(CSAddress + ": " + CSValue + "        " + DSAddress + ": " + DSValue);
        }
    }

    static void printVirtualMachineMemory(VirtualMachine VMachine) {
        String CSAddress, DSAddress;
        String CSValue, DSValue;
        int CS = (int) VMachine.getCS();
        int DS = (int) VMachine.getDS();

        LongObject[] memory = VMachine.getMemoryVM();

        for (int i = 0; i < 128; i++) {
            CSValue = Long.toHexString(memory[i+CS].getValue());
            DSValue = Long.toHexString(memory[i+DS].getValue());
            CSAddress = Integer.toHexString(i + CS);
            DSAddress = Integer.toHexString(i + DS);

            if (CSAddress.length() == 1) {
                CSAddress = "00"+CSAddress;
            }
            else if (CSAddress.length() == 2) {
                CSAddress = "0"+CSAddress;
            }

            if (DSAddress.length() == 1) {
                DSAddress = "00"+DSAddress;
            }
            else if (DSAddress.length() == 2) {
                DSAddress = "0"+DSAddress;
            }


            while (true) {
                if (CSValue.length() == 8) break;
                else CSValue = "0"+CSValue;
            }

            while (true) {
                if (DSValue.length() == 8) break;
                else DSValue = "0"+DSValue;
            }
            if (VMachine.getCC()==i)
                System.out.println(CSAddress + ": \033[31m" + CSValue + "\033[0m        " + DSAddress + ": " + DSValue);
            else
                System.out.println(CSAddress + ": " + CSValue + "        " + DSAddress + ": " + DSValue);

        }
    }

    static void printRealMachineRegisters(Machine machine) {
        System.out.println("CS: " + Long.toHexString(machine.getCS()));
        System.out.println("DS: " + Long.toHexString(machine.getDS()));
        System.out.println("AX: " + Long.toHexString(machine.getAX()));
        System.out.println("BX: " + Long.toHexString(machine.getBX()));
        System.out.println("CC: " + Long.toHexString(machine.getCC()));
        System.out.println("SF: " + Long.toHexString(machine.getSF()));
        System.out.println("TM: " + Long.toHexString(machine.getTM()));
        System.out.println("MODE: " + Long.toHexString(machine.getMode()));
        System.out.println("PI: " + Long.toHexString(machine.getPI()));
        System.out.println("IOI: " + Long.toHexString(machine.getIOI()));
    }

    static void printVirtualMachineRegisters(VirtualMachine VMachine) {
        System.out.println("CS: " + Long.toHexString(VMachine.getCS()));
        System.out.println("DS: " + Long.toHexString(VMachine.getDS()));
        System.out.println("AX: " + Long.toHexString(VMachine.getAX()));
        System.out.println("BX: " + Long.toHexString(VMachine.getBX()));
        System.out.println("CC: " + Long.toHexString(VMachine.getCC()));
        System.out.println("SF: " + Long.toHexString(VMachine.getSF()));
    }
}